package com.taoym.voucher.common.utils;

import java.util.concurrent.*;

/**
 * @coder taoym
 * @datetime 2021/3/24 16:15
 * @desc 线程池工具类
 */
public class ThreadPoolUtils {

    private volatile static ExecutorService executorService = null;

    /**
     * @coder taoym
     * @datetime 2021/3/25 9:53
     * @desc 获取单例的线程池
     */
    public static ExecutorService getExecutorService() {
        if (executorService == null) {
            synchronized (ThreadPoolUtils.class) {
                if (executorService == null) {
                    executorService = createThreadPool(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors() + 1, 200, "t");
                }
            }
        }
        return executorService;
    }

    private static ExecutorService createThreadPool(int corePoolSize, int maximumPoolSize, int workQueueSize, String threadName) {
        return createThreadPool(corePoolSize, maximumPoolSize, workQueueSize, threadName, new ThreadPoolRejectHandler());
    }

    /**
     * @param corePoolSize    常驻核心线程，线程池初始化的时候池里是没有线程的，前面 corePoolSize 个任务是会创建线程，
     *                        当前线程池中的数量大于常驻核心线程数的时候，如果有空闲的线程则使用，没有的话就把任务放到
     *                        工作队列中
     * @param maximumPoolSize 线程池允许创建的最大线程数，如果队列满了，且线程数小于最大线程数，则新建临时线程（空闲超过时间会被销毁的），
     *                        如果队列为无界队列，则该参数无用
     * @param workQueueSize   工作队列，请求线程数大于常驻核心线程数的时候，将多余的任务放到工作队列
     * @param threadName      线程名称
     * @param handler         线程池拒绝策略，当线程池和队列都满了，则调用该策略，执行具体的逻辑
     * @coder taoym
     * @datetime 2021/3/24 22:20
     * @desc 自定义线程池的实现 总体逻辑就是 前corePoolSize个任务时，来一个任务就创建一个线程
     * 如果当前线程池的线程数大于了corePoolSize那么接下来再来的任务就会放入到我们上面设置的workQueue队列中
     * 如果此时workQueue也满了，那么再来任务时，就会新建临时线程，那么此时如果我们设置了keepAliveTime或者设置了allowCoreThreadTimeOut，那么系统就会进行线程的活性检查，一旦超时便销毁线程
     * 如果此时线程池中的当前线程大于了maximumPoolSize最大线程数，那么就会执行我们刚才设置的handler拒绝策略
     */
    private static ExecutorService createThreadPool(int corePoolSize, int maximumPoolSize, int workQueueSize, String threadName, RejectedExecutionHandler handler) {
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(workQueueSize);
        ThreadPoolFactory threadPoolFactory = new ThreadPoolFactory(threadName);
        return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 60L, TimeUnit.SECONDS, queue, threadPoolFactory, handler);
    }

    /**
     * @author: taoym
     * @date: 2021/3/24 20:23
     * @desc: 创建一个可缓存无限制数量的线程池，如果线程池中没有空闲的线程，再来任务的时候就会新建线程，线程60s内没使用就会被销毁
     * 常驻核心线程数为 0
     * 线程池最大线程数 Integer.MAX_VALUE
     * 线程空闲60s回收
     * 使用工作队列来存放任务
     * 由于使用了 Integer.MAX_VALUE 来设置最大线程数，所以会有 oom 的风险
     */
    private static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
    }

    /**
     * @author: taoym
     * @date: 2021/3/24 20:23
     * @desc: 创建一个指定大小的线程池，如果线程池满了，后面的任务会在队列中等待
     * 常驻核心线程数自定义
     * 线程池最大线程数等于常驻核心线程数，所以不会被回收
     * 常驻核心线程数不会被回收，所以时间参数为0
     * 虽然指定了最大线程数，但是没有指定队列的长度，所以也有可能出现oom
     */
    private static ExecutorService newFixeThreadPool(int threads) {
        return new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    }

    /**
     * @author: taoym
     * @date: 2021/3/24 21:37
     * @desc: 创建一个大小为1的线程池，用唯一线程来执行任务，保证任务有序进行
     * 常驻核心线程数为 1
     * 线程池最大线程数等于常驻线程数1
     * 最大线程数为1等于常驻核心线程数，所以不会被回收
     * 虽然制定了最大线程数，但是没有指明队列的长度，有oom风险
     */
    private static ExecutorService newSingleThreadPool() {
        return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    }

    /**
     * @author: taoym
     * @date: 2021/3/24 21:40
     * @desc: 创建指定大小的线程池，支持定时及周期性执行任务
     * 常驻核心线程数自定义
     * 线程池最大线程数等于 Integer.MAX_VALUE
     * 不回收线程
     * 由于线程池最大线程数为 Integer.MAX_VALUE，有oom风险
     */
    private static ExecutorService newScheduleThreadPool(int threads) {
        return new ScheduledThreadPoolExecutor(threads);
    }

    /**
     * @author: taoym
     * @date: 2021/3/24 21:49
     * @desc: 创建持有足够线程的线程池来支持给定的并行级别，并通过使用多个队列减少竞争，并行级别的参数如果不传，默认为cpu数量
     */
    private static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool(Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
    }


}
